#!/usr/bin/env python
# -*- coding: utf-8 -*-


from __future__ import unicode_literals, division, absolute_import, print_function

import sys

from lib.compatibility_utils import PY2, text_type, unicode_str
from lib.compatibility_utils import unicode_argv, add_cp65001_codec
from azw2mobi_cmd import azw3_to_mobis

import lib.unipath as unipath
from lib.unipath import pathof

import os
import traceback

import codecs
add_cp65001_codec()

try:
    from queue import Full
    from queue import Empty
except ImportError:
    from Queue import Full
    from Queue import Empty

if PY2 and sys.platform.startswith("win"):
    from libgui.askfolder_ed import AskFolder

from multiprocessing import Process, Queue

if PY2:
    import Tkinter as tkinter
    import Tkconstants as tkinter_constants
    import tkFileDialog as tkinter_filedialog
    import ttk as tkinter_ttk
else:
    import tkinter
    import tkinter.constants as tkinter_constants
    import tkinter.filedialog as tkinter_filedialog
    import tkinter.ttk as tkinter_ttk

from libgui.scrolltextwidget import ScrolledText

import lib.kindleunpack as kindleunpack

# Set to false to NOT save prefences to an ini file.
# Starting directories for file dialogs will still persist
# for the current KindleUnpack session.
#
# Need to delete the ini file after setting to false, of course.
PERSISTENT_PREFS = True

from inspect import getfile, currentframe
from libgui.prefs import getprefs, saveprefs

# Probably overkill, but to ensure cross-platform success no matter how the script is called/run...
SCRIPT_NAME = unicode_str(getfile(currentframe()))
SCRIPT_DIR = unicode_str(os.path.dirname(unipath.abspath(getfile(currentframe()))))
PROGNAME = unicode_str(os.path.splitext(SCRIPT_NAME)[0])

# Include platform in the ini file name. That way, settings can still persist
# in the event that different OSs access the same script via a network share/flash-drive.
CONFIGFILE = unicode_str(os.path.join(SCRIPT_DIR, '{0}_{1}.json'.format(PROGNAME, sys.platform[:3])))

# Wrap a stream so that output gets appended to shared queue
# using utf-8 encoding
class QueuedStream:
    def __init__(self, stream, q):
        self.stream = stream
        self.encoding = stream.encoding
        self.q = q
        if self.encoding == None:
            self.encoding = 'utf-8'
    def write(self, data):
        if isinstance(data,text_type):
            data = data.encode('utf-8')
        elif self.encoding not in ['utf-8','UTF-8','cp65001','CP65001']:
            udata = data.decode(self.encoding)
            data = udata.encode('utf-8')
        self.q.put(data)
    def __getattr__(self, attr):
        if attr == 'mode':
            return 'wb'
        if attr == 'encoding':
            return 'utf-8'
        return getattr(self.stream, attr)


class MainDialog(tkinter.Frame):

    def __init__(self, root):
        tkinter.Frame.__init__(self, root, border=5)
        self.root = root
        self.interval = 50
        self.p2 = None
        self.q = Queue()
        # To keep things simple for possible future preference additions/deletions:
        # Try to stick to - TK Widget name = prefs dictionary key = ini.get|set name.
        # EX: mobipath = prefs['mobipath'] = config.get('Defaults', mobipath).
        self.prefs = getprefs(CONFIGFILE, self.root, PERSISTENT_PREFS)

        self.status = tkinter.StringVar()
        tkinter.Label(self, textvariable=self.status, justify='center').grid(row=0, columnspan=3, sticky=tkinter_constants.N)
        self.status.set('Change non-DRM Kindle azw3 files to mobi files')
        sticky = tkinter_constants.E + tkinter_constants.W
        ALL = tkinter_constants.E+tkinter_constants.W+tkinter_constants.N+tkinter_constants.S
        # Set to the column the textentry boxes are in.
        self.grid_columnconfigure(1, weight=1)
        # Set to the row the debug log widget is in.
        self.grid_rowconfigure(10, weight=1)

        tkinter.Label(self, text='').grid(row=1, sticky=tkinter_constants.E)
        tkinter.Label(self, text='Kindle azw3 Files Directory', wraplength=200).grid(row=2, sticky=tkinter_constants.E)
        self.mobipath = tkinter.Entry(self, width=50)
        self.mobipath.grid(row=2, column=1, sticky=sticky)
        self.mobipath.insert(0, '')
        button = tkinter.Button(self, text="Browse...", command=self.get_mobipath)
        button.grid(row=2, column=2, sticky=sticky)

        tkinter.Label(self, text='Temp Directory', wraplength=200).grid(row=3, sticky=tkinter_constants.E)
        self.outpath = tkinter.Entry(self, width=50)
        self.outpath.grid(row=3, column=1, sticky=sticky)
        if self.prefs['outpath'] and PERSISTENT_PREFS and unipath.exists(CONFIGFILE):
            outpath = pathof(os.path.normpath(self.prefs['outpath']))
            self.outpath.insert(0, outpath)
        else:
            self.outpath.insert(0, '')
        button = tkinter.Button(self, text="Browse...", command=self.get_outpath)
        button.grid(row=3, column=2, sticky=sticky)

        tkinter.Label(self, text='OPTIONAL: APNX file Associated with AZW3', wraplength=200).grid(row=4, sticky=tkinter_constants.E)
        self.apnxpath = tkinter.Entry(self, width=50)
        self.apnxpath.grid(row=4, column=1, sticky=sticky)
        self.apnxpath.insert(0, '')
        button = tkinter.Button(self, text="Browse...", command=self.get_apnxpath)
        button.grid(row=4, column=2, sticky=sticky)

        self.splitvar = tkinter.IntVar()
        checkbox = tkinter.Checkbutton(self, text="Split Combination Kindlegen eBooks", variable=self.splitvar)
        if self.prefs['splitvar'] and PERSISTENT_PREFS:
            checkbox.select()
        checkbox.grid(row=5, column=1, columnspan=2, sticky=tkinter_constants.W)

        self.rawvar = tkinter.IntVar()
        checkbox = tkinter.Checkbutton(self, text="Write Raw Data", variable=self.rawvar)
        if self.prefs['rawvar'] and PERSISTENT_PREFS:
            checkbox.select()
        checkbox.grid(row=6, column=1, columnspan=2, sticky=tkinter_constants.W)

        self.dbgvar = tkinter.IntVar()
        checkbox = tkinter.Checkbutton(self, text="Dump Mode", variable=self.dbgvar)
        if self.prefs['dbgvar'] and PERSISTENT_PREFS:
            checkbox.select()
        checkbox.grid(row=7, column=1, columnspan=2, sticky=tkinter_constants.W)

        self.hdvar = tkinter.IntVar()
        checkbox = tkinter.Checkbutton(self, text="Use HD Images If Present", variable=self.hdvar)
        if self.prefs['hdvar'] and PERSISTENT_PREFS:
            checkbox.select()
        checkbox.grid(row=8, column=1, columnspan=2, sticky=tkinter_constants.W)

        tkinter.Label(self, text='ePub Output Type:').grid(row=9, sticky=tkinter_constants.E)
        self.epubver_val = tkinter.StringVar()
        self.epubver = tkinter_ttk.Combobox(self, textvariable=self.epubver_val, state='readonly')
        self.epubver['values'] = ('ePub 2', 'ePub 3', 'Auto-detect', 'Force ePub 2')
        self.epubver.current(0)
        if self.prefs['epubver'] and PERSISTENT_PREFS:
            self.epubver.current(self.prefs['epubver'])
        self.epubver.grid(row=9, column=1, columnspan=2, pady=(3,5), sticky=tkinter_constants.W)

        msg1 = 'Conversion Log \n\n'
        self.stext = ScrolledText(self, bd=5, relief=tkinter_constants.RIDGE, wrap=tkinter_constants.WORD)
        self.stext.grid(row=10, column=0, columnspan=3, sticky=ALL)
        self.stext.insert(tkinter_constants.END,msg1)

        self.sbotton = tkinter.Button(
            self, text="Start", width=10, command=self.convertit)
        self.sbotton.grid(row=11, column=1, sticky=tkinter_constants.S+tkinter_constants.E)
        self.qbutton = tkinter.Button(
            self, text="Quit", width=10, command=self.quitting)
        self.qbutton.grid(row=11, column=2, sticky=tkinter_constants.S+tkinter_constants.W)
        if self.prefs['windowgeometry'] and PERSISTENT_PREFS:
            self.root.geometry(self.prefs['windowgeometry'])
        else:
            self.root.update_idletasks()
            w = self.root.winfo_screenwidth()
            h = self.root.winfo_screenheight()
            rootsize = (605, 575)
            x = w//2 - rootsize[0]//2
            y = h//2 - rootsize[1]//2
            self.root.geometry('%dx%d+%d+%d' % (rootsize + (x, y)))
        self.root.protocol('WM_DELETE_WINDOW', self.quitting)

    # read queue shared between this main process and spawned child processes
    def readQueueUntilEmpty(self):
        done = False
        text = ''
        while not done:
            try:
                data = self.q.get_nowait()
                text += unicode_str(data, 'utf-8')
            except Empty:
                done = True
                pass
        return text

    # read from subprocess pipe without blocking
    # invoked every interval via the widget "after"
    # option being used, so need to reset it for the next time
    def processQueue(self):
        poll = self.p2.exitcode
        if poll != None:
            text = self.readQueueUntilEmpty()
            msg = text + '\n\n' + 'eBook successfully Changed\n'
            if poll != 0:
                msg = text + '\n\n' + 'Error: Unpacking Failed\n'
            self.p2.join()
            self.showCmdOutput(msg)
            self.p2 = None
            self.sbotton.configure(state='normal')
            return
        text = self.readQueueUntilEmpty()
        self.showCmdOutput(text)
        # make sure we get invoked again by event loop after interval
        self.stext.after(self.interval,self.processQueue)
        return

    # post output from subprocess in scrolled text widget
    def showCmdOutput(self, msg):
        if msg and msg !='':
            if sys.platform.startswith('win'):
                msg = msg.replace('\r\n','\n')
            self.stext.insert(tkinter_constants.END,msg)
            self.stext.yview_pickplace(tkinter_constants.END)
        return

    def get_mobipath(self):
        cwd = unipath.getcwd()
        if sys.platform.startswith("win") and PY2:
            mobipath = AskFolder(message="Folder to azw3 files",
                defaultLocation=self.prefs['mobipath'] or unipath.getcwd())
        else:
            mobipath = tkinter_filedialog.askdirectory(
                parent=None, title='Folder to azw3 files',
                initialdir=self.prefs['mobipath'] or cwd, initialfile=None)

        if mobipath:
            self.prefs['mobipath'] = pathof(os.path.dirname(mobipath))
            mobipath = pathof(os.path.normpath(mobipath))
            self.mobipath.delete(0, tkinter_constants.END)
            self.mobipath.insert(0, mobipath)
        return

    def get_apnxpath(self):
        cwd = unipath.getcwd()
        apnxpath = tkinter_filedialog.askopenfilename(
            parent=None, title='Optional APNX file associated with AZW3',
            initialdir=self.prefs['apnxpath'] or cwd,
            initialfile=None,
            defaultextension='.apnx', filetypes=[('Kindle APNX Page Information File', '.apnx'), ('All Files', '.*')])
        if apnxpath:
            self.prefs['apnxpath'] = pathof(os.path.dirname(apnxpath))
            apnxpath = pathof(os.path.normpath(apnxpath))
            self.apnxpath.delete(0, tkinter_constants.END)
            self.apnxpath.insert(0, apnxpath)
        return

    def get_outpath(self):
        cwd = unipath.getcwd()
        if sys.platform.startswith("win") and PY2:
            # tk_chooseDirectory is horribly broken for unicode paths
            # on windows - bug has been reported but not fixed for years
            # workaround by using our own unicode aware version
            outpath = AskFolder(message="Folder to Store temp files",
                defaultLocation=self.prefs['outpath'] or unipath.getcwd())
        else:
            outpath = tkinter_filedialog.askdirectory(
                parent=None, title='Folder to Store temp files',
                initialdir=self.prefs['outpath'] or cwd, initialfile=None)
        if outpath:
            self.prefs['outpath'] = outpath
            outpath = pathof(os.path.normpath(outpath))
            self.outpath.delete(0, tkinter_constants.END)
            self.outpath.insert(0, outpath)
        return

    def quitting(self):
        # kill any still running subprocess
        if self.p2 != None:
            if (self.p2.exitcode == None):
                self.p2.terminate()
        if PERSISTENT_PREFS:
            if not saveprefs(CONFIGFILE, self.prefs, self):
                print("Couldn't save INI file.")
        self.root.destroy()
        self.quit()

    # run in a child process and collect its output
    def convertit(self):
        # now disable the button to prevent multiple launches
        self.sbotton.configure(state='disabled')
        mobipath = unicode_str(self.mobipath.get())
        apnxpath = unicode_str(self.apnxpath.get())
        outdir = unicode_str(self.outpath.get())
        if not mobipath or not unipath.exists(mobipath):
            self.status.set('Specified eBook file does not exist')
            self.sbotton.configure(state='normal')
            return
        apnxfile = None
        if apnxpath != "" and unipath.exists(apnxpath):
            apnxfile = apnxpath
        if not outdir:
            self.status.set('No output directory specified')
            self.sbotton.configure(state='normal')
            return
        q = self.q
        log = 'Input Path = "'+ mobipath + '"\n'
        log += 'Output Path = "' + outdir + '"\n'
        if apnxfile != None:
            log += 'APNX Path = "' + apnxfile + '"\n'
        dump = False
        writeraw = False
        splitcombos = False
        use_hd = False
        if self.dbgvar.get() == 1:
            dump = True
            log += 'Debug = True\n'
        if self.rawvar.get() == 1:
            writeraw = True
            log += 'WriteRawML = True\n'
        if self.splitvar.get() == 1:
            splitcombos = True
            log += 'Split Combo KF8 Kindle eBooks = True\n'
        if self.epubver.current() == 0:
            epubversion = '2'
        elif self.epubver.current() == 1:
            epubversion = '3'
        elif self.epubver.current() == 2:
            epubversion = 'A'
        else:
            epubversion = 'F'
        log += 'Epub Output Type Set To: {0}\n'.format(self.epubver_val.get())
        if self.hdvar.get():
            use_hd = True
            # stub for processing the Use HD Images setting
            log += 'Use HD Images If Present = True\n'
        log += '\n\n'
        log += 'Please Wait ...\n\n'
        self.stext.insert(tkinter_constants.END,log)
        self.p2 = Process(target=unpackEbook, args=(q, mobipath, outdir, apnxfile, epubversion, use_hd, dump, writeraw, splitcombos))
        self.p2.start()

        # python does not seem to allow you to create
        # your own eventloop which every other gui does - strange
        # so need to use the widget "after" command to force
        # event loop to run non-gui events every interval
        self.stext.after(self.interval,self.processQueue)
        return


# child process / multiprocessing thread starts here
def unpackEbook(q, infile, outdir, apnxfile, epubversion, use_hd, dump, writeraw, splitcombos):
    sys.stdout = QueuedStream(sys.stdout, q)
    sys.stderr = QueuedStream(sys.stderr, q)

    azw3_to_mobis(infile, outdir, epubversion, use_hd, dump, writeraw, splitcombos)
    # rv = 0
    # try:
    #     kindleunpack.unpackBook(infile, outdir, apnxfile, epubversion, use_hd, dodump=dump, dowriteraw=writeraw, dosplitcombos=splitcombos)
    # except Exception as e:
    #     print("Error: %s" % e)
    #     print(traceback.format_exc())
    #     rv = 1
    # sys.exit(rv)


def main(argv=unicode_argv()):
    root = tkinter.Tk()
    root.title('Kindle eBook Unpack Tool')
    root.minsize(440, 350)
    root.resizable(True, True)
    MainDialog(root).pack(fill=tkinter_constants.BOTH, expand=tkinter_constants.YES)
    root.mainloop()
    return 0

if __name__ == "__main__":
    sys.exit(main())
